iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 23
2

本篇同步發文在個人Blog: 一袋.NET要扛幾樓?打造容器化的ASP.NET Core網站!系列文章 - (23) 建立購物車系統 - 6

1. 修改Startup.cs

  購物車服務Api需要被保護,也就是之前的會員服務的驗證與授權,才能被正確呼叫。而Startup原本加入的Swagger也需要有認證機制,於是也加入OIDC設定。需要啟用Cors,讓外部能呼叫此CartApi。需要另外安裝這兩種套件:

Microsoft.AspNetCore.Authentication.JwtBearer

System.IdentityModel.Tokens.Jwt

    using CartApi.Infrastructure.Filters;
    using CartApi.Models;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Options;
    using StackExchange.Redis;
    using System;
    using System.Collections.Generic;
    using System.IdentityModel.Tokens.Jwt;
    using System.Net.Http;
    
    namespace CartApi
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllers(options =>
                {
                    options.Filters.Add(typeof(HttpGlobalExceptionFilter));
                }).AddNewtonsoftJson(options => {
                    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                });
    
                ConfigureAuthService(services);
    
                services.Configure<CartSettings>(Configuration);
    
                services.AddSingleton<ConnectionMultiplexer>(sp =>
                {
                    var settings = sp.GetRequiredService<IOptions<CartSettings>>().Value;
                    var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true);
    
                    configuration.ResolveDns = true;
                    configuration.AbortOnConnectFail = false;
    
                    return ConnectionMultiplexer.Connect(configuration);
                });
    
                services.AddTransient<ICartRepository, CartRepository>();
                services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
                services.AddSwaggerGen(options => {
                    options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
                    {
                        Version = "v1",
                        Title = "Cart HTTP API",
                        Description = "The Cart Service HTTP API"
                    });
    
                    options.AddSecurityDefinition("oauth2", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
                    {
                        Type = Microsoft.OpenApi.Models.SecuritySchemeType.OAuth2,
                        Flows = new Microsoft.OpenApi.Models.OpenApiOAuthFlows
                        {
                            Implicit = new Microsoft.OpenApi.Models.OpenApiOAuthFlow
                            {
                                AuthorizationUrl = new Uri($"{Configuration.GetValue<string>("IdentityUrl")}/connect/authorize"),
                                TokenUrl = new Uri($"{Configuration.GetValue<string>("IdentityUrl")}/connect/token"),
                                Scopes = new Dictionary<string, string>()
                                {
                                    { "basket", "Basket Api" }
                                }
                            }
                        }
                    });
    
                    options.OperationFilter<AuthorizationCheckOperationFilter>();
                });
    
                services.AddCors(builder =>
                {
                    builder.DefaultPolicyName = "CorsPolicy";
                    builder.AddPolicy("CorsPolicy", options => {
                        options.AllowAnyOrigin()
                            .AllowAnyMethod()
                            .AllowAnyHeader();
                    });
                });
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseSwagger()
                 .UseSwaggerUI(c =>
                 {
                     c.SwaggerEndpoint(
                         $"/swagger/v1/swagger.json",
                         "Cart.API V1");
                     c.OAuthClientId("basketswaggerui");
                     c.OAuthAppName("Basket Swagger UI");
                 });
    
                app.UseRouting();
                app.UseCors("CorsPolicy");
                app.UseAuthentication();
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
    
            private void ConfigureAuthService(IServiceCollection services)
            {
                JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
                var identityUrl = Configuration.GetValue<string>("IdentityUrl");
    
                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(options =>
                {
                    options.Authority = identityUrl;
                    options.RequireHttpsMetadata = false;
                    options.Audience = "basket";
                    options.BackchannelHttpHandler = new HttpClientHandler()
                    {
                        ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
                    };
                });
            }
        }
    }

2. 新增AuthorizationCheckOperationFilter類別

在CartApi的Infrastructure/filters新增AuthorizationCheckOperationFilter類別,並實作IOperationFilter,用來判斷在Swagger使用授權的Controller要對應OIDC功能

    using Microsoft.AspNetCore.Authorization;
    using Microsoft.OpenApi.Models;
    using Swashbuckle.AspNetCore.SwaggerGen;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace CartApi.Infrastructure.Filters
    {
        public class AuthorizationCheckOperationFilter : IOperationFilter
        {
            public void Apply(OpenApiOperation operation, OperationFilterContext context)
            {
                var hasAuthorize =
                      context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
                      || context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
    
                if (hasAuthorize)
                {
                    operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
                    operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
    
                    operation.Security = new List<OpenApiSecurityRequirement>();
                    operation.Security.Add(new OpenApiSecurityRequirement
                    {
                        [
                            new OpenApiSecurityScheme
                            {
                                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
                            }
                        ] = new[] { "basket" }
                    });
                }
    
            }
        }
    }

3. 修改CartController

在CartController加上[Authorize]的屬性,代表這控制器的方法都要被保護。


上一篇
[Day22] 建立購物車系統 - 5
下一篇
[Day24] 建立購物車系統 - 7
系列文
一袋.NET要扛幾樓?打造容器化的ASP.NET Core網站!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言